package com.magupe.develop.sound.to;

import java.io.*;

public class PCMToWAV {

    public static void main(String[] args) {
        String src = "/data/home/magupe/Desktop/sound-16000.pcm";
        String target = "/data/home/magupe/Desktop/sound-16000.wav";
        try {
            convertAudioFiles(src, target);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void convertAudioFiles(String src, String target) throws Exception {
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(target);

        // 文件计算长度
        byte[] buf = new byte[1024 * 4];
        int size = fis.read(buf);
        int PCMSize = 0;
        while (size != -1) {
            PCMSize += size;
            size = fis.read(buf);
        }
        fis.close();

        int sampleRate = 24000;
        short sampleBits = 16;
        short channels = 1;
        WaveHeader header = new WaveHeader();
        header.fileRIFF = new char[]{'R', 'I', 'F', 'F'};                       // 资源交换文件标识符
        header.fileLength = PCMSize + (44 - 8);                                 // 记录文件大小（从WAV文件标志位置开始算），使用4个字节； 文件大小=(44 + PCMSize) - offset - 4，4表示记录文件大小所使用的字节数
        header.fileTag = new char[]{'W', 'A', 'V', 'E'};                        // WAV文件标志
        header.waveForm = new char[]{'f', 'm', 't', ' '};                       // 波形格式标志
        header.filterByte = 16;                                                 // 过滤字节, 一般为 0x10 = 16
        header.formatTag = 0x0001;                                              // 格式类别 (PCM形式采样数据)
        header.channels = channels;                                             // 通道数
        header.sampleRate = sampleRate;                                         // 采样率, 每秒样本数, 表示每个通道的播放速度
        header.transmissionRate = channels * sampleRate * (sampleBits / 8);     // 波形数据传输率 (每秒平均字节数) 声道数×每秒数据位数×每样本数据位/8
        header.blockSize = (short)(channels * (sampleBits / 8));                // 快数据调整数 采样一次占用字节数 声道数×每样本的数据位数/8
        header.sampleBits = sampleBits;                                         // 每样本数据位数
        header.dataFlag = new char[]{'d','a','t','a'};                          // 数据标识符
        header.dataLength = PCMSize;                                            // 采样数据总数,即数据总大小-44

        byte[] h = header.getHeader();
        assert h.length == 44; //WAV标准，头部应该是44字节
        // write header
        fos.write(h, 0, h.length);
        // write data stream
        fis = new FileInputStream(src);
        size = fis.read(buf);
        while (size != -1) {
            fos.write(buf, 0, size);
            size = fis.read(buf);
        }
        fis.close();
        fos.close();
    }
}

class WaveHeader{

    public char fileRIFF[];         // 资源交换文件标识符
    public int fileLength;          // 记录文件大小（从WAV文件标志位置开始算），使用4个字节； 文件大小=(44 + dataLength) - offset - 4，4表示记录文件大小所使用的字节数
    public char fileTag[];          // WAV文件标志
    public char waveForm[];         // 波形格式标志
    public int filterByte;          // 过滤字节, 一般为 0x10 = 16
    public short formatTag;         // 格式类别 (PCM形式采样数据)
    public short channels;          // 通道数
    public int sampleRate;          // 采样率, 每秒样本数, 表示每个通道的播放速度
    public int transmissionRate;    // 波形数据传输率 (每秒平均字节数) 声道数×每秒数据位数×每样本数据位/8
    public short blockSize;         // 快数据调整数 采样一次占用字节数 声道数×每样本的数据位数/8
    public short sampleBits;        // 每样本数据位数
    public char dataFlag[];         // 数据标识符
    public int dataLength;          // 采样数据总数,即数据总大小-44

    public byte[] getHeader() throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        writeChar(bos, fileRIFF);
        writeInt(bos, fileLength);
        writeChar(bos, fileTag);
        writeChar(bos, waveForm);
        writeInt(bos, filterByte);
        writeShort(bos, formatTag);
        writeShort(bos, channels);
        writeInt(bos, sampleRate);
        writeInt(bos, transmissionRate);
        writeShort(bos, blockSize);
        writeShort(bos, sampleBits);
        writeChar(bos, dataFlag);
        writeInt(bos, dataLength);
        bos.flush();
        byte[] r = bos.toByteArray();
        bos.close();
        return r;
    }

    private void writeShort(ByteArrayOutputStream bos, int s) throws IOException {
        byte[] myByte = new byte[2];
        myByte[1] =(byte)( (s << 16) >> 24 );
        myByte[0] =(byte)( (s << 24) >> 24 );
        bos.write(myByte);
    }

    private void writeInt(ByteArrayOutputStream bos, int n) throws IOException {
        byte[] buf = new byte[4];
        buf[3] =(byte)( n >> 24 );
        buf[2] =(byte)( (n << 8) >> 24 );
        buf[1] =(byte)( (n << 16) >> 24 );
        buf[0] =(byte)( (n << 24) >> 24 );
        bos.write(buf);
    }

    private void writeChar(ByteArrayOutputStream bos, char[] id) {
        for (int i = 0; i < id.length; i++) {
            char c = id[i];
            bos.write(c);
        }
    }
}
